#pragma once

#include <AVUIUncommonField.h>

// [REVIEWED 08/02/2008 -- GHERMANN]

namespace AVUI {

class DependencyObject;
class DependencyPropertyChangedEventArgs;
struct EntryIndex;
class PropertyMetadata;
class FrugalMap;

typedef ObjectPtr (*GetReadOnlyValueCallback)(DependencyObject* pDO, BaseValueSource::Enum* pValueSource);
typedef ObjectPtr (*CoerceValueCallback)(DependencyObject* pDO, Object* pBaseValue);
typedef bool (*ValidateValueCallback)(Object*);
typedef void (*PropertyChangedCallback)(DependencyObject* pDO, DependencyPropertyChangedEventArgs*);
typedef bool (*FreezeValueCallback)(DependencyObject* pDO, DependencyProperty* pDP, const EntryIndex& entryIndex, PropertyMetadata* pMetadata, bool isChecking);

class Freezable;
class ArrayList;
class DefaultValueFactory;

/// <summary>Defines certain behavior aspects of a dependency property as it is applied to a specific type, including conditions it was registered with. </summary>
class PropertyMetadata : public Object
{
public:
    DECLARE_ELEMENT(PropertyMetadata, Object);

#pragma region Public Methods

    /// <summary> Initializes a new instance of the <see cref="T:System.Windows.PropertyMetadata" /> class. </summary>
    PropertyMetadata();

    /// <summary> Initializes a new instance of the <see cref="T:System.Windows.PropertyMetadata" /> class with a specified default value for the dependency property that this metadata will be applied to. </summary>
    /// <param name="defaultValue">The default value to specify for a dependency property, usually provided as a value of some specific type.</param>
    /// <exception cref="T:System.ArgumentException">
    /// <paramref name="defaultValue" /> cannot be set to the value <see cref="F:System.Windows.DependencyProperty.UnsetValue" />; see Remarks.</exception>
    PropertyMetadata(Object* pDefaultValue);

    /// <summary> Initializes a new instance of the <see cref="T:System.Windows.PropertyMetadata" /> class with the specified <see cref="T:System.Windows.PropertyChangedCallback" /> implementation reference. </summary>
    /// <param name="propertyChangedCallback">Reference to a handler implementation that is to be called by the property system whenever the effective value of the property changes.</param>
    PropertyMetadata(PropertyChangedCallback propertyChangedCallback);

    /// <summary> Initializes a new instance of the <see cref="T:System.Windows.PropertyMetadata" /> class with the specified default value and <see cref="T:System.Windows.PropertyChangedCallback" /> implementation reference. </summary>
    /// <param name="defaultValue">The default value of the dependency property, usually provided as a value of some specific type.</param>
    /// <param name="propertyChangedCallback">Reference to a handler implementation that is to be called by the property system whenever the effective value of the property changes.</param>
    /// <exception cref="T:System.ArgumentException">
    /// <paramref name="defaultValue" /> cannot be set to the value <see cref="F:System.Windows.DependencyProperty.UnsetValue" />; see Remarks.</exception>
    PropertyMetadata(Object* pDefaultValue, PropertyChangedCallback propertyChangedCallback);

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.PropertyMetadata" /> class with the specified default value and callbacks. </summary>
    /// <param name="defaultValue">The default value of the dependency property, usually provided as a value of some specific type.</param>
    /// <param name="propertyChangedCallback">Reference to a handler implementation that is to be called by the property system whenever the effective value of the property changes.</param>
    /// <param name="coerceValueCallback">Reference to a handler implementation that is to be called whenever the property system calls <see cref="M:System.Windows.DependencyObject.CoerceValue(System.Windows.DependencyProperty)" /> against this property.</param>
    /// <exception cref="T:System.ArgumentException">
    /// <paramref name="defaultValue" /> cannot be set to the value <see cref="F:System.Windows.DependencyProperty.UnsetValue" />; see Remarks.</exception>
    PropertyMetadata(Object* pDefaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback);

#pragma endregion Public Methods
 
#pragma region Public Properties

    /// <summary>Gets or sets a reference to a <see cref="T:System.Windows.CoerceValueCallback" /> implementation specified in this metadata.</summary>
    /// <returns>A <see cref="T:System.Windows.CoerceValueCallback" /> implementation reference.</returns>
    /// <exception cref="T:System.InvalidOperationException">Cannot set a metadata property once it is applied to a dependency property operation.</exception> 
    CoerceValueCallback get_CoerceValueCallback();
    void set_CoerceValueCallback(CoerceValueCallback value);

    /// <summary> Gets and sets the default value of the dependency property. </summary>
    /// <returns>The default value of the property. The default value on a <see cref="T:System.Windows.PropertyMetadata" /> instance created with the parameterless constructor will be <see cref="F:System.Windows.DependencyProperty.UnsetValue" />; see Remarks.</returns>
    /// <exception cref="T:System.ArgumentException">Cannot be set to the value <see cref="F:System.Windows.DependencyProperty.UnsetValue" /> once created; see Remarks.</exception>
    /// <exception cref="T:System.InvalidOperationException">Cannot set a metadata property once it is applied to a dependency property operation.</exception>
    Object* get_DefaultValue();
    void set_DefaultValue(Object* value);
 
    /// <summary>Gets or sets a reference to a <see cref="T:System.Windows.PropertyChangedCallback" /> implementation specified in this metadata.</summary>
    /// <returns>A <see cref="T:System.Windows.PropertyChangedCallback" /> implementation reference.</returns>
    /// <exception cref="T:System.InvalidOperationException">Cannot set a metadata property once it is applied to a dependency property operation.</exception>
    PropertyChangedCallback get_PropertyChangedCallback();
    void set_PropertyChangedCallback(PropertyChangedCallback value);

#pragma endregion Public Properties
 
protected:

#pragma region Protected Methods

    /// <summary>Merges this metadata with the base metadata. </summary>
    /// <returns />
    /// <param name="pBaseMetadata">The base metadata to merge with this instance's values.</param>
    /// <param name="pDP">The dependency property to which this metadata is being applied.</param>
    virtual void Merge(PropertyMetadata* pBaseMetadata, DependencyProperty* pDP);

    /// <summary>Called when this metadata has been applied to a property, which indicates that the metadata is being sealed. </summary>
    /// <returns />
    /// <param name="dp">The dependency property to which the metadata has been applied.</param>
    /// <param name="targetType">The type associated with this metadata if this is type-specific metadata. If this is default metadata, this value is a null reference.</param>
    virtual void OnApply(DependencyProperty* pDP, const Type* pTargetType) { };

#pragma endregion Protected Methods

#pragma region Protected Properties

    /// <summary>Gets a value that determines whether the metadata has been applied to a property in some way, resulting in the immutable state of that metadata instance. </summary>
    /// <returns>true if the metadata instance is immutable; otherwise, false.</returns>
    bool get_IsSealed() { return get_Sealed(); }

#pragma endregion Protected Properties

public:

#pragma region Internal Methods

    void ClearCachedDefaultValue(DependencyObject* pOwner, DependencyProperty* pProperty);
    TRefCountedPtr<PropertyMetadata> Copy(DependencyProperty* pDP);
    virtual TRefCountedPtr<PropertyMetadata> CreateInstance();
    bool DefaultValueWasSet();
    Object* GetDefaultValue(DependencyObject* pOwner, DependencyProperty* pProperty);
    void InvokeMerge(PropertyMetadata* pBaseMetadata, DependencyProperty* pDP) { Merge(pBaseMetadata, pDP); }
    static void PromoteAllCachedDefaultValues(DependencyObject* pOwner);
    bool ReadFlag(unsigned int metadataFlag) { return (m_flags & metadataFlag) != 0; }
    static void RemoveAllCachedDefaultValues(Freezable* pOwner);
    void Seal(DependencyProperty* pDP, const Type* pTargetType);
    void WriteFlag(unsigned int metadataFlag, bool value)
    {
        if (value)
        {
            m_flags |= metadataFlag;
        }
        else
        {
            m_flags &= ~metadataFlag;
        }
    }

#pragma endregion Internal Methods

#pragma region Internal Properties

    virtual GetReadOnlyValueCallback get_GetReadOnlyValueCallback() { return NULL; }

    bool get_IsDefaultValueModified();

    bool get_IsInherited() { return ReadFlag(MetadataFlags::Inherited); }
    void set_IsInherited(bool value) { WriteFlag(MetadataFlags::Inherited, value); }

    bool get_Sealed() { return ReadFlag(MetadataFlags::SealedID); }
    void set_Sealed(bool value) { WriteFlag(MetadataFlags::SealedID, value); }

    bool get_UsingDefaultValueFactory() { return is_type<DefaultValueFactory>(m_pDefaultValue); }

    FreezeValueCallback get_FreezeValueCallback();
    void set_FreezeValueCallback(FreezeValueCallback value);

#pragma endregion Internal Properties

#pragma region Internal Fields

    unsigned int m_flags;

#pragma endregion Internal Fields

#pragma endregion Internal Flags

    class MetadataFlags
    {
    public:
        enum Enum
        {
            DefaultValueModifiedID = 1,
            FW_AffectsArrangeID = 0x80,
            FW_AffectsMeasureID = 0x40,
            FW_AffectsParentArrangeID = 0x200,
            FW_AffectsParentMeasureID = 0x100,
            FW_AffectsRenderID = 0x400,
            FW_BindsTwoWayByDefaultID = 0x2000,
            FW_DefaultUpdateSourceTriggerEnumBit1 = 0x40000000,
            FW_DefaultUpdateSourceTriggerEnumBit2 = 0x80000000,
            FW_DefaultUpdateSourceTriggerModifiedID = 0x4000000,
            FW_InheritsModifiedID = 0x100000,
            FW_IsNotDataBindableID = 0x1000,
            FW_OverridesInheritanceBehaviorID = 0x800,
            FW_OverridesInheritanceBehaviorModifiedID = 0x200000,
            FW_ReadOnlyID = 0x8000000,
            FW_ShouldBeJournaledID = 0x4000,
            FW_ShouldBeJournaledModifiedID = 0x1000000,
            FW_SubPropertiesDoNotAffectRenderID = 0x8000,
            FW_SubPropertiesDoNotAffectRenderModifiedID = 0x10000,
            FW_UpdatesSourceOnLostFocusByDefaultID = 0x2000000,
            Inherited = 0x10,
            SealedID = 2,
            UI_IsAnimationProhibitedID = 0x20
        };
    };

#pragma endregion Internal Flags

private:

#pragma region Private Methods

    static bool DefaultFreezeValueCallback(DependencyObject* pDO, DependencyProperty* pDP, const EntryIndex& entryIndex, PropertyMetadata* pMetadata, bool isChecking);
    static void DefaultValueCachePromotionCallback(Object* pValue);
    static void DefaultValueCacheRemovalCallback(Object* pValue); 
    Object* GetCachedDefaultValue(DependencyObject* pOwner, DependencyProperty* pProperty);
    bool IsModified(unsigned int metadataFlag) { return ReadFlag(metadataFlag); }
    void SetCachedDefaultValue(DependencyObject* pOwner, DependencyProperty* pProperty, Object* pValue);
    void SetModified(unsigned int metadataFlag) { return WriteFlag(metadataFlag, true); }

#pragma endregion Private Methods

#pragma region Private Fields

    CoerceValueCallback m_coerceValueCallback;
    TRefCountedPtr<Object> m_pDefaultValue;
    static UncommonField<FrugalMap> ms_defaultValueFactoryCache;  
    PropertyChangedCallback m_propertyChangedCallback;
    FreezeValueCallback m_freezeValueCallback;

#pragma endregion Private Fields
};

class ReadOnlyPropertyMetadata : public PropertyMetadata
{
public:
    DECLARE_ELEMENT(ReadOnlyPropertyMetadata, PropertyMetadata);

    ReadOnlyPropertyMetadata(Object* pDefaultValue, GetReadOnlyValueCallback getValueCallback, PropertyChangedCallback propertyChangedCallback) : PropertyMetadata(pDefaultValue, propertyChangedCallback)
    {
        m_getValueCallback = getValueCallback;
    }

#pragma region Internal Methods

    virtual GetReadOnlyValueCallback get_GetReadOnlyValueCallback() { return m_getValueCallback; }

#pragma endregion Internal Methods

private:

#pragma region Private Fields

    GetReadOnlyValueCallback m_getValueCallback;

#pragma endregion Private Fields
};



}; // namespace AVUI

